package com.seanzx.resolver.impl;

import com.seanzx.common.Lists;
import com.seanzx.common.Pair;
import com.seanzx.common.Result;
import com.seanzx.constant.SymbolTable;
import com.seanzx.lexical.Token;
import com.seanzx.resolver.IResolver;
import com.seanzx.dp.ProcessorController;
import com.seanzx.dp.condition.ConditionNode;
import com.seanzx.dp.condition.ConditionTree;
import com.seanzx.dp.condition.ConditionType;
import com.seanzx.dp.entity.DataSelectEntity;

import java.util.ArrayList;
import java.util.List;

public class SelectDataResolver implements IResolver<DataSelectEntity> {
    private final ProcessorController processorController = ProcessorController.get();

    @Override
    public boolean matches(List<Token> tokens) {
        return SymbolTable.SELECT.equals(tokens.get(0).getValue());
    }

    @Override
    public DataSelectEntity translate(List<Token> tokens) {
        DataSelectEntity entity = new DataSelectEntity();
        // columns
        entity.setColumns(new ArrayList<>());
        int i = 1;
        for (; i < tokens.size(); i++) {
            if ("FROM".equals(tokens.get(i).getValue())) {
                i++;
                break;
            }
            if (",".equals(tokens.get(i).getValue())) {
                continue;
            }
            entity.getColumns().add(tokens.get(i).getValue());
        }
        // databaseName and tableName
        if (tokens.size() > i + 1 && tokens.get(i + 1).getValue().equals(".")) {
            entity.setDatabaseName(tokens.get(i).getValue());
            entity.setTableName(tokens.get(i + 2).getValue());
            i += 3;
        } else {
            entity.setTableName(tokens.get(i).getValue());
            i++;
        }
        // where
        if (tokens.size() > i && "WHERE".equals(tokens.get(i).getValue())) {
            i++;
            ConditionTree conditionTree = new ConditionTree(entity.getDatabaseName(), entity.getTableName());
            ConditionNode node = null;
            boolean isAnd = false, isOr = false;
            for (; i < tokens.size(); i++) {
                String value = tokens.get(i).getValue();
                // TODO: bracket
                switch(value) {
                    case "AND":
                        isAnd = true;
                        isOr = false;
                        break;
                    case "OR":
                        isOr = true;
                        isAnd = false;
                        break;
                    default:
                        Pair<Integer, ConditionNode> pair = getNode(tokens, i);
                        i = pair.left();
                        if (node == null) {
                            node = pair.right();
                            conditionTree.add(node);
                        } else if (isAnd) {
                            node.and(pair.right());
                        } else if (isOr) {
                            ConditionNode rightNode = pair.right();
                            node.or(rightNode);
                            node = rightNode;
                        }
                }
            }
            entity.setConditionTree(conditionTree);
        }
        return entity;
    }

    private Pair<Integer, ConditionNode> getNode(List<Token> tokens, int index) {
        ConditionNode node = new ConditionNode(tokens.get(index).getValue());
        assert tokens.size() > index + 2;
        String conditionType = tokens.get(index + 1).getValue();
        if ("BETWEEN".equals(conditionType)) {
            node.setConditionType(ConditionType.of("BETWEEN AND"));
            assert tokens.size() > index + 4 && "AND".equals(tokens.get(index + 3).getValue());
            node.setValues(Lists.of(tokens.get(index + 2).getValue(), tokens.get(index + 4).getValue()));
            return Pair.of(index + 4, node);
        }
        if ("IN".equals(conditionType)) {
            node.setConditionType(ConditionType.of("IN"));
            List<String> valueList = new ArrayList<>();
            assert tokens.size() > index + 4 && "(".equals(tokens.get(index + 2).getValue());
            int i = index + 3;
            for (; i < tokens.size(); i++) {
                String value = tokens.get(i).getValue();
                if (")".equals(value)) {
                    break;
                }
                if (",".equals(value)) {
                    continue;
                }
                valueList.add(value);
            }
            node.setValues(valueList);
            return Pair.of(i, node);
        }
        node.setConditionType(ConditionType.of(conditionType));
        node.setValues(Lists.of(tokens.get(index + 2).getValue()));
        return Pair.of(index + 2, node);
    }

    @Override
    public boolean validate(Object parameter) {
        return true;
    }

    @Override
    public Result access(Object parameter) {
        return processorController.select((DataSelectEntity) parameter);
    }
}
